package com.example.cc.client.processor;

import com.example.cc.client.manager.FieldMapCache;
import com.example.cc.client.manager.GenericCacheManager;
import com.example.cc.client.service.ClientConfigService;
import com.example.cc.client.utils.ConfigUtil;
import com.example.cc.common.annotation.MyValue;
import com.example.cc.common.constants.ConfigConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.util.PropertyPlaceholderHelper;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

@Component
@Slf4j
@SuppressWarnings("all")
public class RefreshValueAnnotationBeanPostProcessor implements BeanPostProcessor {

    private final ClientConfigService clientConfigService;
    private volatile boolean initialized = false;
    private final PropertyPlaceholderHelper placeholderHelper;

    @Resource
    private FieldMapCache fieldMapCache;

    @Resource
    private GenericCacheManager<String, Object> cacheManager;


    public RefreshValueAnnotationBeanPostProcessor(ClientConfigService clientConfigService) {
        this.clientConfigService = clientConfigService;
        this.placeholderHelper = new PropertyPlaceholderHelper("${", "}");
    }

    @Override
    public Object postProcessBeforeInitialization(@NonNull Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(@NonNull Object bean, String beanName) throws BeansException {
        if (!initialized) {
            synchronized (this) {
                if (!initialized) {
                    doSyncConfiguration();
                    initialized = true;
                }
            }
        }
        doInjectRefresh(bean);
        return bean;
    }

    private void doSyncConfiguration() {
        try {
            Map<String, Object> configurationMap = clientConfigService.loadAppAllConfig();
            cacheManager.updateCache(configurationMap);
            log.info("Configuration synchronized successfully.");
        } catch (Exception e) {
            log.error("Failed to synchronize configurations.", e);
        }
    }

    private void doInjectRefresh(Object bean) {
        String className = bean.getClass().getName();
        if (!className.startsWith(ConfigConstants.DEFAULT_PACKAGE_NAME) ||
                (!className.endsWith(ConfigConstants.CONTROLLER) && !className.endsWith(ConfigConstants.SERVICE))
        ) {
            return;
        }
        Class<?> clazz = bean.getClass();
        Map<String, Field> cachedFieldMap = fieldMapCache.getClazzFieldCache().computeIfAbsent(clazz, this::cacheAnnotatedFields);
        if (cachedFieldMap.isEmpty()) {
            return;
        }
        for (Map.Entry<String, Field> entry : cachedFieldMap.entrySet()) {
            String key = entry.getKey();
            Field field = entry.getValue();
            Object value = cacheManager.get(key);
            if (Objects.isNull(value)) {
                log.warn("No configuration found for key: {}", key);
                continue;
            }
            try {
                field.setAccessible(true);
                Object targetValue = ConfigUtil.convertValue(value, field.getType());
                if (targetValue != null) {
                    field.set(bean, targetValue);
                    log.info("===成功设置值：{} = {}", targetValue, field.getName());
                } else {
                    log.warn("No value found for key '{}' in property source", key);
                    // 是否需要删除
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Failed to set field " + field.getName(), e);
            } finally {
                // 恢复字段的访问权限
                field.setAccessible(false);
            }
        }

        /*Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            MyValue myValueAnnotation = field.getAnnotation(MyValue.class);
            if (myValueAnnotation != null) {
                String keyWithPlaceholders = myValueAnnotation.value();
                log.info(placeholderHelper.replacePlaceholders(keyWithPlaceholders, System::getenv));
                String resolvedKey = ConfigUtil.getOriginalKey(keyWithPlaceholders);
                Object value = cacheManager.get(resolvedKey);
                if (Objects.isNull(value)) {
                    log.warn("No configuration found for key: {}", resolvedKey);
                    continue;
                }
                field.setAccessible(true);
                try {
                    field.set(bean, convertValue(value, field.getType()));
                    log.info("===成功设置值：{} = {}", value, field.getName());
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Failed to set field " + field.getName(), e);
                } finally {
                    field.setAccessible(false);
                }
            }
        }*/
    }

    /**
     * 缓存带有 {@code MyValue} 注解的字段，以避免每次都需要进行反射查找。
     *
     * @param clazz 要检查的类
     * @return 包含 {@code MyValue} 注解的字段映射
     */
    private Map<String, Field> cacheAnnotatedFields(Class<?> clazz) {
        Map<String, Field> stringFieldMap = new ConcurrentHashMap<>();
        Class<?> currentClass = clazz;
        while (currentClass != null) {
            for (Field field : currentClass.getDeclaredFields()) {
                if (field.isAnnotationPresent(MyValue.class)) {
                    MyValue annotation = field.getAnnotation(MyValue.class);
                    String keyWithPlaceholders = annotation.value();
                    String resolvedKey = ConfigUtil.getOriginalKey(keyWithPlaceholders);
                    stringFieldMap.put(resolvedKey, field);
                    // 同时缓存字段对应的类，以便更新时可以直接通过key找到字段所在的class
                    fieldMapCache.getKeyFieldCache().putIfAbsent(resolvedKey, currentClass);
                }
            }
            // 不仅处理当前类中的字段，还处理所有继承自父类的字段，一般不需要
            currentClass = currentClass.getSuperclass();
        }
        return stringFieldMap;
    }


    public static void main(String[] args) {
        Object originalValue = "1024";
        Integer value = ConfigUtil.convertValue(originalValue, Integer.class);
        System.out.println(value);
        Object originalValue2 = "10.24";
        Double d = ConfigUtil.convertValue(originalValue2, Double.class);
        System.out.println(d);
    }
}
